package meiqia.flutter_meiqia

import android.Manifest
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
//import android.support.v4.app.ActivityCompat
import androidx.core.app.ActivityCompat
import android.text.TextUtils
import com.meiqia.core.MQManager
import com.meiqia.core.MQManager.getMeiqiaSDKVersion
import com.meiqia.core.MQMessageManager
import com.meiqia.core.MQScheduleRule
import com.meiqia.core.bean.MQMessage
import com.meiqia.core.callback.*
import com.meiqia.meiqiasdk.activity.MQMessageFormActivity
import com.meiqia.meiqiasdk.util.MQConfig
import com.meiqia.meiqiasdk.util.MQIntentBuilder
import com.meiqia.meiqiasdk.util.MQUtils
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.io.File

class FlutterMeiqiaPlugin(registrar: Registrar) : MethodCallHandler, EventChannel.StreamHandler {

    private val mRegistrar = registrar
    private var mOfflineMessageReceiver: BroadcastReceiver? = null

    companion object {
        private const val TAG = "MEI QIA"
        private const val FLUTTER_CHANNEL = "flutter_meiqia"
        private const val FLUTTER_EVENT = "flutter_meiqia_event"
        private const val CODE_SUCCEED = 1
        private const val CODE_FAILED = 2
        private const val MESSAGE_SUCCEED = "succeed"
        private const val MESSAGE_FAILED = "failed"
        private const val REQUEST_PERMISSION_CODE = 1024
        private const val OTHER_ACTION = 0xff00
        private const val MESSAGE_RECEIVER = 0xff01
        private const val AGENT_INPUTTING = 0xff02
        private const val AGENT_CHANGE = 0xff03
        private const val STR_MESSAGE_RECEIVER = "Event[message receiver]"
        private const val STR_AGENT_INPUTTING = "Event[agent inputting]"
        private const val STR_AGENT_CHANGE = "Event[agent change]"
        private const val STR_OTHER_ACTION = "Event[other action]"
        private var hasRegistered = false

        @JvmStatic
        fun registerWith(registrar: Registrar) {
            val channel = MethodChannel(registrar.messenger(), FLUTTER_CHANNEL)
            val stream = EventChannel(registrar.messenger(), FLUTTER_EVENT)
            val plugin = FlutterMeiqiaPlugin(registrar)
            channel.setMethodCallHandler(plugin)
            stream.setStreamHandler(plugin)
        }
    }

    @SuppressLint("PrivateResource")
    override fun onMethodCall(call: MethodCall, result: Result) {
        if (TextUtils.equals(call.method, "registerMeiQia")) {
            registerMeiQia(call, result)
        } else {
            if (!hasRegistered) {
                result.error("IllegalStateException", "not ready",
                        "MeiQia is not initialed, and call registerMeiQia first")
                return
            }

            mRegistrar.addRequestPermissionsResultListener { code, _, grantedResult ->
                if (code == REQUEST_PERMISSION_CODE &&
                        grantedResult.isNotEmpty() && grantedResult[0] == PackageManager.PERMISSION_GRANTED) {
                    gotoConversion(call)
                    true
                } else {
                    MQUtils.show(mRegistrar.context(), com.meiqia.meiqiasdk.R.string.mq_sdcard_no_permission)
                    false
                }
            }

            when (call.method) {
                "openMeiQiaService" -> openMeiQiaService()
                "closeMeiQiaService" -> closeMeiQiaService()
                "conversation" -> conversation(call)
                "leaveMessage" -> leaveMessage()
                "getNewMeiQiaId" -> getNewMeiQiaId(result)
                "setClientOffline" -> setClientOffline(result)
                "getMQMessage" -> getMQMessage(call, result)
                "registerDeviceToken" -> registerDeviceToken(call, result)
                "getCurrentAgent" -> getCurrentAgent(result)
                "getCurrentClientId" -> getCurrentClientId(result)
                "createMQClient" -> createMQClient(result)
                "updateMessage" -> updateMessage(call)
                "endCurrentConversation" -> endCurrentConversation(result)
                "setCurrentClient" -> setCurrentClient(call, result)
                "getUnreadMessages" -> getUnreadMessages(call, result)
                "getSdkVersion" -> getSdkVersion(result)
                else -> result.notImplemented()
            }
        }
    }

    override fun onListen(argument: Any?, sink: EventChannel.EventSink?) {
        mOfflineMessageReceiver = createEventListener(sink)
        mRegistrar.context().registerReceiver(
                mOfflineMessageReceiver,
                IntentFilter().apply {
                    addAction(MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED)
                    addAction(MQMessageManager.ACTION_AGENT_INPUTTING)
                    addAction(MQMessageManager.ACTION_AGENT_CHANGE_EVENT)
                }
        )
    }

    override fun onCancel(argument: Any?) {
        if (mOfflineMessageReceiver != null) {
            mRegistrar.context().unregisterReceiver(mOfflineMessageReceiver)
            mOfflineMessageReceiver = null
        }
    }

    private fun createEventListener(sink: EventChannel.EventSink?)
            : BroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent == null || intent.action.isNullOrEmpty()) return

            val mqMessageManager = MQMessageManager.getInstance(context)
            val resp = HashMap<String, Any>()
            val data = HashMap<String, Any?>()

            when (intent.action) {
                MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED -> {
                    val msgId = intent.getStringExtra("msgId")
                    val message = mqMessageManager.getMQMessage(msgId)

                    // message info
                    data["agentId"] = message.agent_id
                    data["agentNickname"] = message.agent_nickname
                    data["avatar"] = message.avatar
                    data["content"] = message.content
                    data["contentRobot"] = message.content_robot
                    data["contentType"] = message.content_type
                    data["conversationId"] = message.conversation_id
                    data["createdOn"] = message.created_on
                    data["enterpriseId"] = message.enterprise_id
                    data["extra"] = message.extra
                    data["fromType"] = message.from_type
                    data["id"] = message.id
                    data["isAlreadyFeedback"] = message.isAlreadyFeedback
                    data["isRobot"] = message.isRobot
                    data["isRead"] = message.is_read
                    data["mediaUrl"] = message.media_url
                    data["questionId"] = message.question_id
                    data["status"] = message.status
                    data["subType"] = message.sub_type
                    data["trackId"] = message.track_id
                    data["type"] = message.type

                    resp["code"] = MESSAGE_RECEIVER
                    resp["message"] = STR_MESSAGE_RECEIVER
                }

                MQMessageManager.ACTION_AGENT_INPUTTING -> {
                    resp["code"] = AGENT_INPUTTING
                    resp["message"] = STR_AGENT_INPUTTING
                }

                MQMessageManager.ACTION_AGENT_CHANGE_EVENT -> {
                    val agent = mqMessageManager.currentAgent

                    // agent info
                    data["avatar"] = agent.avatar
                    data["cellphone"] = agent.cellphone
                    data["email"] = agent.email
                    data["enterpriseId"] = agent.enterprise_id
                    data["id"] = agent.id
                    data["agentId"] = agent.agentId
                    data["nickname"] = agent.nickname
                    data["publicCellphone"] = agent.public_cellphone
                    data["publicEmail"] = agent.public_email
                    data["qq"] = agent.qq
                    data["realname"] = agent.realname
                    data["telephone"] = agent.telephone
                    data["weixin"] = agent.weixin
                    data["isOnline"] = agent.isOnLine
                    data["privilege"] = agent.privilege

                    resp["code"] = AGENT_CHANGE
                    resp["message"] = STR_AGENT_CHANGE
                }

                else -> {
                    resp["code"] = OTHER_ACTION
                    resp["message"] = STR_OTHER_ACTION
                }
            }

            resp["data"] = data
            sink?.success(resp)
        }
    }

    private fun registerMeiQia(call: MethodCall, result: Result) {
        val key = call.argument<String>("key")!!
        val debugModel = call.argument<Boolean>("debug")!!
        val gravity = call.argument<Int>("gravity")

        MQManager.setDebugMode(debugModel)

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        MQConfig.init(mRegistrar.context(), key, object : OnInitCallback {
            override fun onSuccess(clientId: String?) {
                data["clientId"] = clientId
                succeedCallback(resp, data, result)

                hasRegistered = true

                MQConfig.ui.titleGravity = if (gravity == 1)
                    MQConfig.ui.MQTitleGravity.LEFT else MQConfig.ui.MQTitleGravity.CENTER
            }

            override fun onFailure(code: Int, message: String?) {
                failedCallback(resp, code, message, result)
            }
        })
    }

    private fun openMeiQiaService() =
            MQManager.getInstance(mRegistrar.context()).openMeiqiaService()

    private fun closeMeiQiaService() =
            MQManager.getInstance(mRegistrar.context()).closeMeiqiaService()

    private fun conversation(call: MethodCall) =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                    mRegistrar.activity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(mRegistrar.activity(),
                        arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                        REQUEST_PERMISSION_CODE)
            } else {
                gotoConversion(call)
            }

    @Suppress("UNCHECKED_CAST")
    private fun gotoConversion(call: MethodCall) {
        val argument = call.arguments as HashMap<String, Any>

        if (argument.isEmpty()) {
            mRegistrar.context().startActivity(MQIntentBuilder(mRegistrar.context())
                    .build()
                    .apply {
                        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    })
        } else {
            val clientId = call.argument<String>("clientId")
            val customisedId = call.argument<String>("customisedId")
            val agentToken = call.argument<String>("agentToken")
            val groupToken = call.argument<String>("groupToken")

            val client = call.argument<HashMap<String, Any?>>("clientParams")
            val clientInfo = hashMapOf<String, String>()

            if (client != null) {
                if (client.containsKey("extras")) {
                    val extras = client["extras"] as HashMap<String, String>

                    for (key in extras.keys) {
                        val value = extras[key].toString()
                        if (value.isNotEmpty()) {
                            clientInfo[key] = value
                        }
                    }
                    client.remove("extras")
                }

                for (key in client.keys) {
                    val value = client[key].toString()
                    if (value.isNotEmpty()) {
                        clientInfo[key] = value
                    }
                }
            }

            var preSendMessage: String? = null
            var preSendImage: String? = null
            val preSend = call.argument<HashMap<String, String>>("preSend")

            if (preSend != null) {
                preSendMessage = preSend["message"]
                preSendImage = preSend["image"]
            }

            mRegistrar.context().startActivity(
                    MQIntentBuilder(mRegistrar.context())
                            .apply {
                                if (!clientId.isNullOrEmpty()) setClientId(clientId)
                                if (!customisedId.isNullOrEmpty()) setCustomizedId(customisedId)
                                if (!agentToken.isNullOrEmpty()) setScheduledAgent(agentToken)
                                if (!groupToken.isNullOrEmpty()) setScheduledGroup(groupToken)
                                setScheduleRule(MQScheduleRule.REDIRECT_ENTERPRISE)

                                if (clientInfo.isNotEmpty()) setClientInfo(clientInfo)

                                if (!preSendMessage.isNullOrEmpty()) setPreSendTextMessage(preSendMessage)
                                if (!preSendImage.isNullOrEmpty()) setPreSendImageMessage(File(preSendImage))
                            }
                            .build()
                            .apply {
                                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            }
            )
        }
    }

    private fun leaveMessage() =
            mRegistrar.context().startActivity(Intent(mRegistrar.context(),
                    MQMessageFormActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            })

    private fun getNewMeiQiaId(result: Result) =
            MQManager.getInstance(mRegistrar.context())
                    .createMQClient(object : OnGetMQClientIdCallBackOn {
                        override fun onSuccess(mqClientId: String?) {
                            result.success(mqClientId)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            result.error("$code", message, "")
                        }
                    })

    private fun setClientOffline(result: Result) {
        MQManager.getInstance(mRegistrar.context()).setClientOffline()
        result.success(true)
    }

    private fun getMQMessage(call: MethodCall, result: Result) {
        val timeStamp = call.argument<Long>("timeStamp")!!
        val length = call.argument<Int>("length")!!
        val fromService = call.argument<Boolean>("fromService")!!

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        if (fromService) {
            MQManager.getInstance(mRegistrar.context())
                    .getMQMessageFromService(timeStamp, length, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        } else {
            MQManager.getInstance(mRegistrar.context())
                    .getMQMessageFromDatabase(timeStamp, length, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        }
    }

    private fun registerDeviceToken(call: MethodCall, result: Result) {
        val token = call.argument<String>("token")
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context()).registerDeviceToken(token, object : OnRegisterDeviceTokenCallback {
            override fun onSuccess() {
                succeedCallback(resp, HashMap(), result)
            }

            override fun onFailure(code: Int, message: String?) {
                failedCallback(resp, code, message, result)
            }
        })
    }

    private fun getCurrentAgent(result: Result) {
        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        val agent = MQManager.getInstance(mRegistrar.context()).currentAgent
        if (agent == null) {
            resp["code"] = CODE_FAILED
            resp["message"] = MESSAGE_FAILED
        } else {
            resp["code"] = CODE_SUCCEED
            resp["message"] = MESSAGE_SUCCEED

            // agent info
            data["avatar"] = agent.avatar
            data["cellphone"] = agent.cellphone
            data["email"] = agent.email
            data["enterpriseId"] = agent.enterprise_id
            data["id"] = agent.id
            data["agentId"] = agent.agentId
            data["nickname"] = agent.nickname
            data["publicCellphone"] = agent.public_cellphone
            data["publicEmail"] = agent.public_email
            data["qq"] = agent.qq
            data["realname"] = agent.realname
            data["telephone"] = agent.telephone
            data["weixin"] = agent.weixin
            data["isOnline"] = agent.isOnLine
            data["privilege"] = agent.privilege
        }

        resp["data"] = data
        result.success(resp)
    }

    private fun getCurrentClientId(result: Result) =
            result.success(MQManager.getInstance(mRegistrar.context()).currentClientId)

    private fun createMQClient(result: Result) {
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context()).createMQClient(
                object : OnGetMQClientIdCallBackOn {
                    override fun onSuccess(clientId: String?) {
                        succeedCallback(resp, hashMapOf(Pair("client", clientId)), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun updateMessage(call: MethodCall) {
        val messageId = call.argument<Long>("messageId")!!
        val isRead = call.argument<Boolean>("isRead")!!
        MQManager.getInstance(mRegistrar.context())
                .updateMessage(messageId, isRead)
    }

    private fun endCurrentConversation(result: Result) {
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context())
                .endCurrentConversation(object : OnEndConversationCallback {
                    override fun onSuccess() {
                        succeedCallback(resp, HashMap(), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun setCurrentClient(call: MethodCall, result: Result) {
        val clientIdOrCustomizedId = call.argument<String>("clientOrCustomizedId")
        val resp = HashMap<String, Any>()

        MQManager.getInstance(mRegistrar.context()).setCurrentClient(clientIdOrCustomizedId,
                object : SimpleCallback {
                    override fun onSuccess() {
                        succeedCallback(resp, HashMap(), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun getUnreadMessages(call: MethodCall, result: Result) {
        val clientId = call.argument<String?>("clientOrCustomizedId")

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        if (clientId.isNullOrEmpty()) {
            MQManager.getInstance(mRegistrar.context())
                    .getUnreadMessages(object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        } else {
            MQManager.getInstance(mRegistrar.context())
                    .getUnreadMessages(clientId, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        }
    }

    private fun getSdkVersion(result: Result) {
        result.success(getMeiqiaSDKVersion())
    }

    /** 成功回调 */
    private fun succeedCallback(resp: HashMap<String, Any>, data: HashMap<String, Any?>, result: Result) {
        resp["code"] = CODE_SUCCEED
        resp["message"] = MESSAGE_SUCCEED
        resp["data"] = data
        result.success(resp)
    }

    /** 失败回调 */
    private fun failedCallback(resp: HashMap<String, Any>, code: Int, message: String?, result: Result) {
        resp["code"] = CODE_FAILED
        resp["message"] = MESSAGE_FAILED
        resp["data"] = hashMapOf(Pair("errorCode", code), Pair("errorMsg", message))
        result.success(resp)
    }
}
